package comm;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.zip.Adler32;

import com.google.protobuf.InvalidProtocolBufferException;

import biotech.osla.MessageProtos.*;

/** 数据包管理相关类
 * 提供所有与客户端通信数据包的封装
 * 用到第三方数据包编码解码包protobuf
 * 
 * 外部通过DataCheck类将字节数据转化为消息类，通过各个消息类的getMsgBytes()方法将消息类转化为字节数据
 * 
 */

// data node -数据类
// DataNodeBase类用于实现消息到编码与解码
abstract class DataNodeBase {
	static final int MaxDataByteSize = 256; // 系统正常运行可以改大为1KB
	static final int MinDataByteSize = 16; // 要求正好为空包时包长度
	// 4字节帧头
	static final byte dataHead1 = 0x7e;
	static final byte dataHead2 = 0x3a;
	static final byte dataHead3 = 0x7e;
	static final byte dataHead4 = 0x3a;

	static final byte MaxMsgNumOfListMsg = 5; // 单个要发送到list包中消息条数上限

	abstract int getDataID(); // 只提供非静态方法，静态方法不能被子类覆盖

	// 求校验
	final static int getCheck32(byte[] buffer, int offset, int num) {
		Adler32 check32 = new Adler32();
		check32.update(buffer, offset, num);
		return (int) (check32.getValue() & 0xffffffff);
	}

	// bytes转int（低字节在前，内部保证数组不越界）
	final static int bytesToInt(byte[] buffer, int offset) {
		if (buffer.length >= offset + 4) {
			int num = 0;
			num += buffer[offset + 3] & 0xff;
			num = num << 8;
			num += buffer[offset + 2] & 0xff;
			num = num << 8;
			num += buffer[offset + 1] & 0xff;
			num = num << 8;
			num += buffer[offset + 0] & 0xff;
			return num;
		} else {
			return 0;
		}

	}

	// int转bytes（低字节在前，内部保证数组不越界）
	final static void intToBytes(int num, byte[] buffer, int offset) {
		if (buffer.length >= offset + 4) {
			buffer[offset + 0] = (byte) (num & 0xff);
			num = num >>> 8;
			buffer[offset + 1] = (byte) (num & 0xff);
			num = num >>> 8;
			buffer[offset + 2] = (byte) (num & 0xff);
			num = num >>> 8;
			buffer[offset + 3] = (byte) (num & 0xff);
		}
	}
}

abstract class DataSend extends DataNodeBase { // 待发送的消息

	final byte[] encodeToByteArray() { // 消息编码，不可重载
		byte[] bufferMsg = this.getMsgBytes();
		int dataLength = bufferMsg.length + DataNodeBase.MinDataByteSize;
		byte[] buffer = new byte[dataLength];
		// 帧头
		buffer[0] = DataNodeBase.dataHead1; // 帧头1
		buffer[1] = DataNodeBase.dataHead2; // 帧头2
		buffer[2] = DataNodeBase.dataHead3; // 帧头3
		buffer[3] = DataNodeBase.dataHead4; // 帧头4

		DataNodeBase.intToBytes(dataLength - 4 * 2, buffer, 4 * 1);// 长度（4字节）
		// Log.println("长度：" + dataLength + "--DataSend.encodeToByteArray");
		// Log.printBytes(buffer, 4, 4);
		DataNodeBase.intToBytes(this.getDataID(), buffer, 4 * 2); // 消息ID（4字节）

		for (int i = 0; i < bufferMsg.length; i++) {// 内容
			buffer[4 * 3 + i] = bufferMsg[i];
		}

		int crcNum = DataNodeBase.getCheck32(buffer, 8, dataLength - 4 * 3);// 校验（4字节）
		// Log.println("校验：" + (long) (crcNum & 0xffffffff));// debug
		DataNodeBase.intToBytes(crcNum, buffer, dataLength - 4);
		// Log.printBytes(buffer, dataLength - 4, 4);
		return buffer;
	}

	abstract byte[] getMsgBytes(); // 获取消息编码

	abstract void setUpdated(DBAccess aDBAccess); // 收到回复消息，标记已同步

}

abstract class DataRecv extends DataNodeBase { // 接受到的消息
	// abstract DataRecv getData(); //
	// DataSend getReplyData(DBAccess aDBAccess) { // 生成回复消息，若无回复消息则返回null
	// return null;
	// }
}

/** 数据解析类
 * 将接收缓存中到byte数组解析为对应消息对象
 */
// 消息格式：帧头(4)+长度(4)+包ID(4)+数据+校验(4)
// 空包长度为16字节
// 长度表示（包ID+数据+校验）的字节数
// 校验采用crc32校验对（包ID+数据）进行校验
final class DataCheck {

	static boolean checkDataHead(final byte[] buffer, int dataHead, int length) { // 检查帧头
		if (buffer[dataHead] == DataNodeBase.dataHead1
				&& buffer[dataHead + 1] == DataNodeBase.dataHead2
				&& buffer[dataHead + 2] == DataNodeBase.dataHead3
				&& buffer[dataHead + 3] == DataNodeBase.dataHead4) {
			return true;
		} else {
			return false;
		}
	}

	static int checkDataLength(final byte[] buffer, int dataHead, int length) { // 获取帧的总长度
		int dataLength = DataNodeBase.bytesToInt(buffer, dataHead + 4);
		dataLength += (DataNodeBase.MinDataByteSize - 8);
		// Log.println("帧的总长度" + dataLength); // debug
		return dataLength;
	}

	static DataRecv check(final byte[] buffer, int dataHead, int dataLength) { // dataLength为整个包长度
		// 检查校验
		final int crcNum = DataNodeBase.getCheck32(buffer, dataHead + 8,
				dataLength - 12);
		final int crcNumGet = DataNodeBase.bytesToInt(buffer, dataHead
				+ dataLength - 4);
		// Log.println("校验检查：新" + crcNum + "原:" + crcNumGet);// debug
		if (crcNum == crcNumGet) {// 检查校验
			// 检查内容
			int dataID = DataNodeBase.bytesToInt(buffer, dataHead + 8);
			// Log.println("消息ID：" + dataID + "DataCheck.check"); // debug
			byte[] bufferContent = Arrays.copyOfRange(buffer, dataHead + 3 * 4,
					dataHead + dataLength - 1 * 4);
			try {
				return DataCheck.checkContent(dataID, bufferContent);
			} catch (InvalidProtocolBufferException e) {
				e.printStackTrace();
				Log.println("消息格式有误" + e.toString());
				//Log.printBytes(buffer, dataHead, dataLength); // debug
				return null;
			}
		} else {
			Log.println("消息校验有误"); // debug
			return null;
		}

	}

	private static DataRecv checkContent(int dataID, final byte[] buffer)
			throws InvalidProtocolBufferException { // 检查消息内容
		MessageType messageType = MessageType.valueOf(dataID);
		switch (messageType) {
		case HEARTBEAT: // 心跳包消息
			return DataHeartBeatRecv.checkContent(buffer);
		case COMMAND: // 通用指令消息
			return DataCommandRecv.checkContent(buffer);
		case INSTRUMENTBINDING: // 仪器绑定消息
			return DataInstrumentBinding.checkContent(buffer);
		case SESSIONLIST: // 使用记录消息
			return DataClientSessionList.checkContent(buffer);
		case USERINTRAININGLIST: // 用户培训消息
			return DataUserInTrainingList.checkContent(buffer);
		case USEREVENTLIST: // 刷卡机用户事件消息
			return DataUserEventList.checkContent(buffer);
		case SYSTEMEVENTLIST: // 刷卡机系统事件消息
			return DataSystemEventList.checkContent(buffer);
		default:
			Log.println("checkContent没有准备对应解析函数");
			return null;
		}
	}
}

// 以下为具体消息

// 心跳包（通信服务收）
// // 由客户端保证发来的心跳包中包含instrumentID和state
// // 如instrumentID直接用于后边行为不合法会在确认MAC地址时关闭连接，state直接发给web
// // instrumentID仅收到第一个心跳包时用与确定节点下标
// // state每次都会更新到状态位给web随时检查
class DataHeartBeatRecv extends DataRecv {
	static final int dataID = MessageType.HEARTBEAT.getNumber();
	private Heartbeat msg = null;

	private DataHeartBeatRecv(Heartbeat msgHeartbeat) {
		this.msg = msgHeartbeat;
	}

	static DataRecv checkContent(final byte[] buffer)
			throws InvalidProtocolBufferException { // 解码
		// Log.println("心跳包检查 ");
		Heartbeat msgHeartbeat = Heartbeat.parseFrom(buffer);
		// Log.println("收到心跳包时间:clientTime" + msgHeartbeat.getClientTimestamp()
		// + "serverTime" + msgHeartbeat.getServerTimestamp() + "ID:" +
		// msgHeartbeat.hasInstrumentId() + msgHeartbeat.getInstrumentId() +
		// "state:" + msgHeartbeat.hasClientState() +
		// msgHeartbeat.getClientState());
		if (true == msgHeartbeat.hasInstrumentId()
				&& true == msgHeartbeat.hasClientState()) {
			return new DataHeartBeatRecv(msgHeartbeat);
		} else {
			Log.println("心跳包不合法" + " msgHeartbeat.hasInstrumentId():"
					+ msgHeartbeat.hasInstrumentId()
					+ " msgHeartbeat.hasClientState():"
					+ msgHeartbeat.hasClientState());
			return null;
		}
	}

	@Override
	int getDataID() { // 获取消息ID
		return dataID;
	}

	int getClientTimeStamp() {
		return this.msg.getClientTimestamp();
	}

	int getMsgInstrumentID() {
		return this.msg.getInstrumentId();
	}

	int getMsgInstrumentState() {
		return this.msg.getClientState();
	}
}

// 心跳包（通信服务发）
class DataHeartBeatSend extends DataSend {
	static final int dataID = MessageType.HEARTBEAT.getNumber();
	private Heartbeat msg = null;

	private DataHeartBeatSend(Heartbeat msgHeartbeat) {
		this.msg = msgHeartbeat;
	}

	static DataHeartBeatSend getInstance(final int aClientTimeStamp) {
		Heartbeat.Builder msgBuilder = Heartbeat.newBuilder();
		int timestamp = (int) (System.currentTimeMillis() / 1000);
		// Log.println("发送心跳包时间" + timestamp);
		return new DataHeartBeatSend(msgBuilder //
				.setClientTimestamp(aClientTimeStamp) //
				.setServerTimestamp(timestamp) //
				.build());
	}

	@Override
	int getDataID() { // 获取消息ID
		return dataID;
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
	}

}

// 通用指令（通信服务收）
class DataCommandRecv extends DataRecv {
	static final int dataID = MessageType.COMMAND.getNumber();
	Command msg = null;
	private DataSend dataSendOfReply = null;

	private DataCommandRecv(Command aMsg) {
		this.msg = aMsg;
	}

	static DataCommandRecv checkContent(final byte[] buffer)
			throws InvalidProtocolBufferException {// 解码
		// Log.println("收到command命令：" + "--DataCommandRecv.checkContent");
		// Log.printBytes(buffer, 0, buffer.length);
		// Log.println("--DataCommandRecv.checkContent");
		Command msg = Command.parseFrom(buffer);
		return new DataCommandRecv(msg);
	}

	@Override
	int getDataID() {
		return dataID;
	}

	void setDataSendOfReply(DataSend dataSendOfReply) {
		this.dataSendOfReply = dataSendOfReply;
	}

	DataSend getDataSendOfReply() {
		return this.dataSendOfReply;
	}

	// 判断是否仪器列表请求（仪器初始化请求）
	boolean isInstrumentListReq() {
		if (Command.Option.CLIENT_INIT_REQ == msg.getOption()) {
			return true;
		} else {
			return false;
		}
	}

	// 判断是否为客户端初始化完成的回复
	boolean isReplyInitedOK() {
		if (this.isReplyOK()) { // 判断为客户端成功回复
			if (DataCommandSend.dataID == this.dataSendOfReply.getDataID()) { // 判断对应发送消息为通用消息，且标识为初始化完成
				if (DataCommandSend.isCommandInitDown(dataSendOfReply)) {
					return true;
				}
			}
		}
		return false;

	}

	boolean isCommandReplyOK() { // 判断是否为客户端成功回复（IO线程调用）？？
		// Log.println("通用消息检查");
		if (Command.Option.CLIENT_RECV_OK == this.msg.getOption()) {
			return true;
		} else {
			return false;
		}
	}

	boolean isReplyOK() { // 判断是否为客户端成功回复，且含有发送消息
		// Log.println("通用消息检查");
		if (Command.Option.CLIENT_RECV_OK == this.msg.getOption()
				&& null != this.dataSendOfReply) {
			return true;
		} else {
			return false;
		}
	}

	boolean isReplyERR() { // 判断是否为客户端失败回复，且含有发送消息
		if (Command.Option.CLIENT_RECV_ERR == msg.getOption()
				&& null != dataSendOfReply) {
			return true;
		} else {
			return false;
		}
	}
}

// 通用指令（通信服务发）
class DataCommandSend extends DataSend {
	static final int dataID = MessageType.COMMAND.getNumber();
	private Command msg = null;

	private DataCommandSend(Command.Option option) {
		Command msgCommand = Command.newBuilder().setOption(option).build();
		this.msg = msgCommand;
	}

	static DataCommandSend getDataCommandSendInitDown() { // 初始化完成消息
		return new DataCommandSend(Command.Option.SERVER_INIT_DONE);
	}

	static DataCommandSend getDataCommandSendRecvOK() { // OK消息
		return new DataCommandSend(Command.Option.SERVER_RECV_OK);
	}

	static DataCommandSend getDataCommandSendRecvERR() { // ERR消息
		return new DataCommandSend(Command.Option.SERVER_RECV_ERR);
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	int getDataID() {
		return dataID;
	}

	static boolean isCommandReplyOK(DataSend dataSend) { // 判断是否为OK消息
		if (null != dataSend) {
			if (dataSend.getDataID() == dataID) {
				if (((DataCommandSend) dataSend).msg.getOption() == Command.Option.SERVER_RECV_OK) {
					return true;
				}
			}
		}
		return false;
	}

	static boolean isCommandInitDown(DataSend dataSend) { // 判断是否为初始化完成消息
		if (null != dataSend) {
			if (dataSend.getDataID() == dataID) {
				if (((DataCommandSend) dataSend).msg.getOption() == Command.Option.SERVER_INIT_DONE) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
		// 初始化完成消息有回复消息
		Log.println("收到回复消息" + this.getClass().getName());
	}
}

// 仪器列表消息
class DataInstrumentList extends DataSend {
	static final int dataID = MessageType.INSTRUMENTLIST.getNumber();
	private InstrumentList msg = null;

	private DataInstrumentList(InstrumentList aMsg) {
		this.msg = aMsg;
	}

	static DataInstrumentList getInstance(DBAccess aDBAccess) {
		// Log.println("仪器列表");
		InstrumentList.Builder msgBuilder = InstrumentList.newBuilder();
		if (aDBAccess.getInstrumentList(msgBuilder) == true) {
			Log.println("仪器列表ok，个数：" + msgBuilder.getInstrumentCount());
			return new DataInstrumentList(msgBuilder.build());
		} else {
			Log.println("仪器列表err");
			return null;
		}
	}

	@Override
	int getDataID() { // 获取消息ID
		return dataID;
	}

	@Override
	byte[] getMsgBytes() { // 获取消息编码
		return msg.toByteArray();
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
	}
}

// 仪器绑定信息
class DataInstrumentBinding extends DataRecv {
	static final int dataID = MessageType.INSTRUMENTBINDING.getNumber();
	private InstrumentBinding msg = null;

	private DataInstrumentBinding(InstrumentBinding aMsg) {
		this.msg = aMsg;
	}

	static DataInstrumentBinding checkContent(final byte[] buffer)
			throws InvalidProtocolBufferException { // 解码
		InstrumentBinding msg = InstrumentBinding.parseFrom(buffer);
		return new DataInstrumentBinding(msg);
	}

	@Override
	int getDataID() { // 获取消息ID
		return dataID;
	}

	DataSend getReplyData(DBAccess aDBAccess) { // 生成回复消息，若无回复消息则返回null
		if (aDBAccess.setInstrumentBinding(msg) == true) {
			return DataCommandSend.getDataCommandSendRecvOK();
		} else {
			return null;
		}
	}

	// 获取绑定消息中的仪器ID
	int getInstrumentId() {
		if (null != msg) {
			return this.msg.getInstrumentId();
		} else {
			return -1;
		}
	}

	// 获取绑定消息中的仪器MAC地址
	String getClientMacAddr() {
		if (null != msg) {
			return this.msg.getClientMacAddr();
		} else {
			return null;
		}
	}

	// 获取绑定状态
	// // true为绑定，false为解绑定
	EBindingState getBindingState() {
		switch(this.msg.getOption()){
		case REQUEST_BINDING: return EBindingState.Request;
		case CONFIRM_BINDING: return EBindingState.Confirm;
		case RELEASE_BINDING: return EBindingState.Release;
		default: return null;
		}
	}
	
	enum EBindingState{
		Request, Confirm, Release
	}
}


// 仪器详细参数
class DataInstrumentDetail extends DataSend {
	static final int dataID = MessageType.INSTRUMENTDETAIL.getNumber();
	private InstrumentDetail msg = null;
	private Int32Num updateTick; // 更新Tick
	private int instrumentID;

	private DataInstrumentDetail(int aInstrumentID, InstrumentDetail aMsg,
			Int32Num aUpdateTick) {
		this.msg = aMsg;
		this.updateTick = aUpdateTick;
		this.instrumentID = aInstrumentID;
	}

	static Queue<DataSend> getInstance(final DBAccess aDBAccess,
			final int aInstrumentID, final boolean aForInit) {
		Int32Num newUpdateTick = new Int32Num();
		InstrumentDetail.Builder msgBuilder = InstrumentDetail.newBuilder();
		if (aDBAccess.getInstrumentDetail(aInstrumentID, msgBuilder,
				newUpdateTick, aForInit) == true) {
			// 返回队列是为了与其他消息统一（永远不会分包）
			Queue<DataSend> dataSendQueue = new LinkedList<DataSend>();
			if (true == msgBuilder.isInitialized()) {
				// 数据库连接正常，且查询结果非0
				dataSendQueue.offer(new DataInstrumentDetail(aInstrumentID,
						msgBuilder.build(), newUpdateTick));
			} else {
				// 若查询结果为0则不修改队列（最终返回长度为0队列）
			}
			return dataSendQueue;
		} else {
			return null;
		}
	}

	@Override
	int getDataID() {
		return dataID;
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
		Log.println("收到回复消息" + this.getClass().getName());
		aDBAccess.setUpdatedForInstrumentDetail(this.instrumentID, this.msg,
				this.updateTick);

	}
}

// 仪器专享时间列表
class DataReservationList extends DataSend {
	static final int dataID = MessageType.RESERVATIONLIST.getNumber();
	private ReservationList msg = null;
	private Int32Num updateTick;
	private int instrumentID;

	private DataReservationList(ReservationList aMsg, Int32Num aUpdateTick,
			int aInstrumentID) {
		this.msg = aMsg;
		this.updateTick = aUpdateTick;
		this.instrumentID = aInstrumentID;
	}

	static Queue<DataSend> getInstance(final DBAccess aDBAccess,
			final int aInstrumentID, final boolean aForInit) {
		Int32Num newUpdateTick = new Int32Num();
		ReservationList.Builder msgBuilder = ReservationList.newBuilder();
		if (aDBAccess.getReservationList(aInstrumentID, msgBuilder,
				newUpdateTick, aForInit) == true) {
			Queue<DataSend> dataSendQueue = new LinkedList<DataSend>();
			final ReservationList newMsgList = msgBuilder.build();
			final int msgNum = newMsgList.getReservationCount();
			final int msgSize = newMsgList.getSerializedSize();
			msgBuilder = null;
			if (msgSize > DataNodeBase.MaxDataByteSize) {
				final int numOfOnePackage = msgNum
						* DataNodeBase.MaxDataByteSize / msgSize;
				// 需要分包
				for (int i = 0; i < msgNum;) {
					msgBuilder = ReservationList.newBuilder();
					for (int j = 0; j < numOfOnePackage && i < msgNum; j++, i++) {
						msgBuilder.addReservation(newMsgList.getReservation(i));
					}
					dataSendQueue.offer(new DataReservationList(msgBuilder
							.build(), newUpdateTick, aInstrumentID));
					msgBuilder = null;
				}
			} else if (msgNum > 0) {
				// 不需要分包
				dataSendQueue.offer(new DataReservationList(newMsgList,
						newUpdateTick, aInstrumentID));
			} else {
				// 空包
			}
			return dataSendQueue;
		} else {
			return null;
		}
	}

	@Override
	int getDataID() {
		return dataID;
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
		Log.println("收到回复消息" + this.getClass().getName());
		aDBAccess.setUpdatedForReservationList(this.instrumentID, this.msg,
				this.updateTick);
	}
}

// 临时卡关联列表
class DataTempCardBindingList extends DataSend {
	static final int dataID = MessageType.TEMPCARDBINDINGLIST.getNumber();
	private TempCardBindingList msg = null;
	private Int32Num updateTick;
	private int instrumentID;
	private IntArrayList intArrayList;

	private DataTempCardBindingList(TempCardBindingList aMsg,
			Int32Num aUpdateTick, int aInstrumentID, IntArrayList aIntArrayList) {
		this.msg = aMsg;
		this.updateTick = aUpdateTick;
		this.instrumentID = aInstrumentID;
		this.intArrayList = aIntArrayList;
	}

	static Queue<DataSend> getInstance(DBAccess aDBAccess, int aInstrumentID,
			boolean aForInit) {
		Int32Num newUpdateTick = new Int32Num();
		TempCardBindingList.Builder msgBuilder = TempCardBindingList
				.newBuilder();
		IntArrayList newIntArrayList = new IntArrayList();
		if (aDBAccess.getTempCardBindingList(aInstrumentID, msgBuilder,
				newUpdateTick, aForInit, newIntArrayList) == true) {
			Queue<DataSend> dataSendQueue = new LinkedList<DataSend>();
			final TempCardBindingList newMsgList = msgBuilder.build();
			final int msgNum = newMsgList.getTempCardBindingCount();
			final int msgSize = newMsgList.getSerializedSize();
			msgBuilder = null;
			if (msgSize > DataNodeBase.MaxDataByteSize) {
				final int numOfOnePackage = msgNum
						* DataNodeBase.MaxDataByteSize / msgSize;
				// 需要分包
				for (int i = 0; i < msgNum;) {
					msgBuilder = TempCardBindingList.newBuilder();
					IntArrayList intArrayList = new IntArrayList();
					for (int j = 0; j < numOfOnePackage && i < msgNum; j++, i++) {
						msgBuilder.addTempCardBinding(newMsgList
								.getTempCardBinding(i));
						intArrayList.add(newIntArrayList.get(i));
					}
					dataSendQueue.offer(new DataTempCardBindingList(msgBuilder
							.build(), newUpdateTick, aInstrumentID,
							intArrayList));
					msgBuilder = null;
				}
			} else if (msgNum > 0) {
				// 不需要分包
				dataSendQueue.offer(new DataTempCardBindingList(newMsgList,
						newUpdateTick, aInstrumentID, newIntArrayList));
			} else {
				// 空包
			}
			return dataSendQueue;
		} else {
			return null;
		}
	}

	@Override
	int getDataID() {
		return dataID;
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
		Log.println("收到回复消息" + this.getClass().getName());
		aDBAccess.setUpdatedForTempCardBindingList(this.instrumentID, this.msg,
				this.updateTick, this.intArrayList);
	}
}

// 用户列表
class DataUserList extends DataSend {
	static final int dataID = MessageType.USERLIST.getNumber();
	private UserList msg = null;
	private Int32Num updateTick;
	private int instrumentId;

	private DataUserList(UserList aMsg, Int32Num aUpdateTick, int aInstrumentId) {
		this.msg = aMsg;
		this.updateTick = aUpdateTick;
		this.instrumentId = aInstrumentId;
	}

	static Queue<DataSend> getInstance(final DBAccess aDBAccess,
			final int aInstrumentID, final boolean aForInit) {
		Int32Num newUpdateTick = new Int32Num();
		UserList.Builder msgBuilder = UserList.newBuilder();
		if (aDBAccess.getUserList(aInstrumentID, msgBuilder, newUpdateTick,
				aForInit) == true) {
			Queue<DataSend> dataSendQueue = new LinkedList<DataSend>();
			final UserList newMsgList = msgBuilder.build();
			final int msgNum = newMsgList.getUserCount();
			final int msgSize = newMsgList.getSerializedSize();
			msgBuilder = null;
			if (msgSize > DataNodeBase.MaxDataByteSize) {
				final int numOfOnePackage = msgNum
						* DataNodeBase.MaxDataByteSize / msgSize;
				// 需要分包
				for (int i = 0; i < msgNum;) {
					msgBuilder = UserList.newBuilder();
					for (int j = 0; j < numOfOnePackage && i < msgNum; j++, i++) {
						msgBuilder.addUser(newMsgList.getUser(i));
					}
					dataSendQueue.offer(new DataUserList(msgBuilder.build(),
							newUpdateTick, aInstrumentID));
					msgBuilder = null;
				}
			} else if (msgNum > 0) {
				// 不需要分包
				dataSendQueue.offer(new DataUserList(newMsgList, newUpdateTick,
						aInstrumentID));
			} else {
				// 空包
			}
			return dataSendQueue;
		} else {
			return null;
		}
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	int getDataID() {
		return dataID;
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
		Log.println("收到回复消息" + this.getClass().getName());
		aDBAccess.setUpdatedForUserList(this.instrumentId, this.msg,
				this.updateTick);
	}

}

// 资格列表
class DataUserRightList extends DataSend {
	static final int dataID = MessageType.USERRIGHTLIST.getNumber();
	private UserRightList msg = null;
	private Int32Num updateTick;
	private int instrumentID;

	private DataUserRightList(UserRightList aMsg, Int32Num aUpdateTick,
			int aInstrumentID) {
		this.msg = aMsg;
		this.updateTick = aUpdateTick;
		this.instrumentID = aInstrumentID;
	}

	static Queue<DataSend> getInstance(DBAccess aDBAccess, int aInstrumentID,
			boolean aForInit) {
		Int32Num newUpdateTick = new Int32Num();
		UserRightList.Builder msgBuilder = UserRightList.newBuilder();
		if (aDBAccess.getUserRightList(aInstrumentID, msgBuilder,
				newUpdateTick, aForInit) == true) {
			Queue<DataSend> dataSendQueue = new LinkedList<DataSend>();
			final UserRightList newMsgList = msgBuilder.build();
			final int msgNum = newMsgList.getUserRightCount();
			final int msgSize = newMsgList.getSerializedSize();
			msgBuilder = null;
			if (msgSize > DataNodeBase.MaxDataByteSize) {
				final int numOfOnePackage = msgNum
						* DataNodeBase.MaxDataByteSize / msgSize;
				// 需要分包
				for (int i = 0; i < msgNum;) {
					msgBuilder = UserRightList.newBuilder();
					for (int j = 0; j < numOfOnePackage && i < msgNum; j++, i++) {
						msgBuilder.addUserRight(newMsgList.getUserRight(i));
					}
					dataSendQueue.offer(new DataUserRightList(msgBuilder
							.build(), newUpdateTick, aInstrumentID));
					msgBuilder = null;
				}
			} else if (msgNum > 0) {
				// 不需要分包
				dataSendQueue.offer(new DataUserRightList(newMsgList,
						newUpdateTick, aInstrumentID));
			} else {
				// 空包
			}
			return dataSendQueue;
		} else {
			return null;
		}
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	int getDataID() {
		return dataID;
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
		Log.println("收到回复消息" + this.getClass().getName());
		aDBAccess.setUpdatedForUserRightList(this.instrumentID, this.msg,
				this.updateTick);
	}

}

// 账户列表
class DataAccountList extends DataSend {
	static final int dataID = MessageType.ACCOUNTLIST.getNumber();
	private AccountList msg = null;
	private Int32Num updateTick;
	private int instrumentID;

	private DataAccountList(AccountList aMsg, Int32Num aUpdateTick,
			int aInstrumentID) {
		this.msg = aMsg;
		this.updateTick = aUpdateTick;
		this.instrumentID = aInstrumentID;
	}

	static Queue<DataSend> getInstance(final DBAccess aDBAccess,
			final int aInstrumentID, boolean aForInit) {
		Int32Num newUpdateTick = new Int32Num();
		AccountList.Builder msgBuilder = AccountList.newBuilder();
		if (false == aDBAccess.getAccountList(aInstrumentID, msgBuilder,
				newUpdateTick, aForInit)) {
			return null;
		} else {
			Queue<DataSend> dataSendQueue = new LinkedList<DataSend>();
			final AccountList newMsgList = msgBuilder.build();
			final int msgNum = newMsgList.getAccoutCount();
			final int msgSize = newMsgList.getSerializedSize();
			msgBuilder = null;
			if (msgSize > DataNodeBase.MaxDataByteSize) {
				final int numOfOnePackage = msgNum
						* DataNodeBase.MaxDataByteSize / msgSize;
				// 需要分包
				for (int i = 0; i < msgNum;) {
					msgBuilder = AccountList.newBuilder();
					for (int j = 0; j < numOfOnePackage && i < msgNum; j++, i++) {
						msgBuilder.addAccout(newMsgList.getAccout(i));
					}
					dataSendQueue.offer(new DataAccountList(msgBuilder.build(),
							newUpdateTick, aInstrumentID));
					msgBuilder = null;
				}
			} else if (msgNum > 0) {
				// 不需要分包
				dataSendQueue.offer(new DataAccountList(newMsgList,
						newUpdateTick, aInstrumentID));
			} else {
				// 空包
			}
			return dataSendQueue;
		}
	}

	@Override
	byte[] getMsgBytes() {
		return this.msg.toByteArray();
	}

	@Override
	int getDataID() {
		return dataID;
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
		Log.println("收到回复消息" + this.getClass().getName());
		aDBAccess.setUpdatedForAccountList(this.instrumentID, this.msg,
				this.updateTick);

	}
}

// 预约记录列表
// 预约状态需要判断
// 1105：失败：置为1106，均标记预约已同步（失败不再重复同步）
// 其他：成功标记已同步，失败无动作（失败继续重复同步）
// 即成功则标记已同步，失败若为1105置为1106并标记已同步，否则无动作
// 可在数据库接口中对预约同步作switch动作逐个分析msg
class DataBookingList extends DataSend {
	static final int dataID = MessageType.BOOKINGLIST.getNumber();
	private BookingList msg = null;
	private Int32Num updateTick;
	private int instrumentID;

	private DataBookingList(BookingList aMsg, Int32Num aUpdateTick,
			int aInstrumentID) {
		this.msg = aMsg;
		this.updateTick = aUpdateTick;
		this.instrumentID = aInstrumentID;
	}

	static Queue<DataSend> getInstance(DBAccess aDBAccess, int aInstrumentID,
			boolean aForInit) {
		Int32Num newUpdateTick = new Int32Num();
		BookingList.Builder msgBuilder = BookingList.newBuilder();
		if (aDBAccess.getBookingList(aInstrumentID, msgBuilder, newUpdateTick,
				aForInit) == true) {
			Queue<DataSend> dataSendQueue = new LinkedList<DataSend>();
			final BookingList newMsgList = msgBuilder.build();
			final int msgNum = newMsgList.getBookingCount();
			final int msgSize = newMsgList.getSerializedSize();
			msgBuilder = null;
			if (msgSize > DataNodeBase.MaxDataByteSize) {
				final int numOfOnePackage = msgNum
						* DataNodeBase.MaxDataByteSize / msgSize;
				// 需要分包
				for (int i = 0; i < msgNum;) {
					msgBuilder = BookingList.newBuilder();
					for (int j = 0; j < numOfOnePackage && i < msgNum; j++, i++) {
						msgBuilder.addBooking(newMsgList.getBooking(i));
					}
					dataSendQueue.offer(new DataBookingList(msgBuilder.build(),
							newUpdateTick, aInstrumentID));
					msgBuilder = null;
				}
			} else if (msgNum > 0) {
				// 不需要分包
				dataSendQueue.offer(new DataBookingList(newMsgList,
						newUpdateTick, aInstrumentID));
			} else {
				// 空包
			}
			return dataSendQueue;
		} else {
			return null;
		}
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	int getDataID() {
		return dataID;
	}

	@Override
	// 同步成功
	void setUpdated(DBAccess aDBAccess) {
		Log.println("收到回复消息" + this.getClass().getName());
		aDBAccess.setUpdatedForBookingList(this.instrumentID, this.msg,
				this.updateTick);
	}

	// 同步失败
	void setUpdatedFail(DBAccess aDBAccess, NoteList aNoteListToNotify) {
		Log.println("同步失败" + this.getClass().getName());
		aDBAccess.setUpdatedForBookingListFail(this.instrumentID, this.msg,
				this.updateTick, aNoteListToNotify);

	}
}

// 系统日历
// // 本次查询到的所有包全部发送成功时标记已同步
// // 整个大包分成到所有小包全部发成功则标记已同步（可在最后一个小包里标记，检查最后一个发送成功即可）
// // 标记已同步时重复的年份不计（只把不重复的发给数据库即可，可用bool数组实现，避免查询是否已存在，直接标记（x-2012）为true即可）
class DataCalendarList extends DataSend {
	static final int dataID = MessageType.CALENDARLIST.getNumber();
	private CalendarList msg = null;
	private Int32Num updateTick; // 更新Tick
	private int instrumentID;
	private UpdateYears updateYears; // 待更新标记
	private boolean isTailMsg = false;

	private DataCalendarList(int aInstrumentID, CalendarList aMsg,
			Int32Num aUpdateTick, UpdateYears aUpdateYears, boolean aIsTailMsg) {
		this.msg = aMsg;
		this.updateTick = aUpdateTick;
		this.instrumentID = aInstrumentID;
		this.updateYears = aUpdateYears;
		this.isTailMsg = aIsTailMsg;
	}

	static Queue<DataSend> getInstance(final DBAccess aDBAccess,
			final int aInstrumentID, final boolean aForInit) {
		Int32Num newUpdateTick = new Int32Num();
		UpdateYears newUpdateYears = new UpdateYears();
		CalendarList.Builder msgBuilder = CalendarList.newBuilder();
		if (aDBAccess.getCalendarList(aInstrumentID, msgBuilder, newUpdateTick,
				newUpdateYears, aForInit) == true) {
			Queue<DataSend> dataSendQueue = new LinkedList<DataSend>();
			final CalendarList msgList = msgBuilder.build();
			final int msgNum = msgList.getCalendarCount();
			final int msgSize = msgList.getSerializedSize();
			msgBuilder = null;
			if (msgSize > DataNodeBase.MaxDataByteSize) {
				final int numOfOnePackage = msgNum
						* DataNodeBase.MaxDataByteSize / msgSize;
				// 需要分包
				int i = 0;
				while (i < msgNum) {
					msgBuilder = CalendarList.newBuilder();
					for (int j = 0; j < numOfOnePackage && i < msgNum; j++, i++) {
						msgBuilder.addCalendar(msgList.getCalendar(i));
					}
					if (i < msgNum) {
						// 不是最后一个包，还会继续循环分包
						dataSendQueue.offer(new DataCalendarList(aInstrumentID,
								msgBuilder.build(), newUpdateTick,
								newUpdateYears, false));
					} else {
						// 最后一个包
						dataSendQueue.offer(new DataCalendarList(aInstrumentID,
								msgBuilder.build(), newUpdateTick,
								newUpdateYears, true));
					}
					msgBuilder = null;
				}
			} else if (msgNum > 0) {
				// 不需要分包
				dataSendQueue.offer(new DataCalendarList(aInstrumentID,
						msgList, newUpdateTick, newUpdateYears, true));
			} else {
				// 空包
			}
			return dataSendQueue;

		} else {
			return null;
		}
	}

	@Override
	int getDataID() {
		return dataID;
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
		Log.println("收到回复消息" + this.getClass().getName());
		if (true == this.isTailMsg) {
			aDBAccess.setUpdatedForCalendarList(this.instrumentID, this.msg,
					this.updateTick, this.updateYears);
		} else {
			return;
		}

	}

}

// 待同步年份列表
// 供DataCalendarList用，在创建消息时生成待同步日历中所有年份列表，同步完成（最后一个分包同步完成）后通过此列表标记数据库对应年份日历同步成功
// 年份与数组序号对应（若yiears[i]为true，即表示(2000 + i)年待标记更新完成）

class UpdateYears { // 仅支持2000到2100年
	final int FirstYear = 2000;
	final int LastYear = 2100;
	boolean[] years = new boolean[LastYear - FirstYear];

	UpdateYears() {
		this.clear();
	}

	boolean set(int aYear) {
		if (aYear > 0 && aYear < LastYear) {
			years[aYear % FirstYear] = true;
			return true;
		} else {
			return false;
		}
	}

	void clear() {
		for (int i = 0; i < this.years.length; i++) {
			this.years[i] = false;
		}
	}

	IntArrayList get() {
		IntArrayList intArrayList = new IntArrayList();
		for (int i = 0; i < this.years.length; i++) {
			if (true == this.years[i]) {
				intArrayList.add(i + FirstYear);
			}
		}
		return intArrayList;
	}
}

// 刷卡机全局配置信息
class DataGlobalConfig extends DataSend {
	static final int dataID = MessageType.GLOBALCONFIG.getNumber();
	private GlobalConfig msg = null;
	private Int32Num updateTick; // 更新Tick
	private int instrumentID;

	private DataGlobalConfig(int aInstrumentID, GlobalConfig aMsg,
			Int32Num aUpdateTick) {
		this.msg = aMsg;
		this.updateTick = aUpdateTick;
		this.instrumentID = aInstrumentID;
	}

	static Queue<DataSend> getInstance(final DBAccess aDBAccess,
			final int aInstrumentID, final boolean aForInit) {
		Int32Num newUpdateTick = new Int32Num();
		GlobalConfig.Builder msgBuilder = GlobalConfig.newBuilder();
		if (aDBAccess.getGlobalConfig(aInstrumentID, msgBuilder, newUpdateTick,
				aForInit) == true) {
			// 返回队列是为了与其他消息统一（永远不会分包）
			Queue<DataSend> dataSendQueue = new LinkedList<DataSend>();
			if (true == msgBuilder.isInitialized()) {
				// 数据库连接正常，且查询结果非0
				dataSendQueue.offer(new DataGlobalConfig(aInstrumentID,
						msgBuilder.build(), newUpdateTick));
			} else {
				// 若查询结果为0则不修改队列（最终返回长度为0队列）
			}
			return dataSendQueue;
		} else {
			return null;
		}
	}

	@Override
	int getDataID() {
		return dataID;
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
		Log.println("收到回复消息" + this.getClass().getName());
		aDBAccess.setUpdatedForGlobalConfig(this.instrumentID, this.msg,
				this.updateTick);

	}
}

// 使用记录列表
class DataClientSessionList extends DataRecv {
	static final int dataID = MessageType.SESSIONLIST.getNumber();
	private SessionList msg = null;

	private DataClientSessionList(SessionList aMsg) {
		this.msg = aMsg;
	}

	static DataClientSessionList checkContent(final byte[] buffer)
			throws InvalidProtocolBufferException { // 解码
		SessionList msg = SessionList.parseFrom(buffer);
		return new DataClientSessionList(msg);
	}

	@Override
	int getDataID() { // 获取消息ID
		return dataID;
	}

	DataSend getReplyData(DBAccess aDBAccess, IONodeList.DoingForDB aDoingForDB) { // 生成回复消息，若无回复消息则返回null
		if (true == aDBAccess.setClientSessionList(msg, aDoingForDB)) {
			return DataCommandSend.getDataCommandSendRecvOK();// 同步成功
		} else {
			return null;
		}
	}

}

// 最近的使用记录列表，实际由最新管理员使用记录和最新普通用户使用记录两条
// 仅初始化时使用
class DataClientSessionListLast extends DataSend {
	static final int dataID = MessageType.SESSIONLIST.getNumber();
	private SessionList msg = null;
	private int instrumentId;

	private DataClientSessionListLast(SessionList aMsg, int aInstrumentId) {
		this.msg = aMsg;
		this.instrumentId = aInstrumentId;
	}

	static Queue<DataSend> getInstance(final DBAccess aDBAccess,
			final int aInstrumentID) {
		SessionList.Builder msgBuilder = SessionList.newBuilder();
		if (aDBAccess.getClientSessionListLast(aInstrumentID, msgBuilder) == true) {
			Queue<DataSend> dataSendQueue = new LinkedList<DataSend>();
			final SessionList newMsgList = msgBuilder.build();
			final int msgNum = newMsgList.getSessionCount();
			final int msgSize = newMsgList.getSerializedSize();
			msgBuilder = null;
			if (msgSize > DataNodeBase.MaxDataByteSize) {
				final int numOfOnePackage = msgNum
						* DataNodeBase.MaxDataByteSize / msgSize;
				// 需要分包
				for (int i = 0; i < msgNum;) {
					msgBuilder = SessionList.newBuilder();
					for (int j = 0; j < numOfOnePackage && i < msgNum; j++, i++) {
						msgBuilder.addSession(newMsgList.getSession(i));
					}
					dataSendQueue.offer(new DataClientSessionListLast(msgBuilder.build(), aInstrumentID));
					msgBuilder = null;
				}
			} else if (msgNum > 0) {
				// 不需要分包
				dataSendQueue.offer(new DataClientSessionListLast(newMsgList, aInstrumentID));
			} else {
				// 空包
			}
			return dataSendQueue;
		} else {
			return null;
		}
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	int getDataID() {
		return dataID;
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
		Log.println("收到回复消息" + this.getClass().getName() + "instrumentID:" + instrumentId);
	}

}
// 培训用户列表
class DataUserInTrainingList extends DataRecv {
	static final int dataID = MessageType.USERINTRAININGLIST.getNumber();
	private UserInTrainingList msg = null;

	private DataUserInTrainingList(UserInTrainingList aMsg) {
		this.msg = aMsg;
	}

	static DataUserInTrainingList checkContent(final byte[] buffer)
			throws InvalidProtocolBufferException { // 解码
		UserInTrainingList msg = UserInTrainingList.parseFrom(buffer);
		return new DataUserInTrainingList(msg);
	}

	@Override
	int getDataID() { // 获取消息ID
		return dataID;
	}

	DataSend getReplyData(DBAccess aDBAccess, IONodeList.DoingForDB aDoingForDB) { // 生成回复消息，若无回复消息则返回null
		if (true == aDBAccess.setUserInTrainingList(msg, aDoingForDB)) {
			return DataCommandSend.getDataCommandSendRecvOK();// 同步成功
		} else {
			return null;
		}
	}
}

// 最新刷卡机用户事件列表
class DataUserEventListLast extends DataSend {
	static final int dataID = MessageType.USEREVENTLIST.getNumber();
	private UserEventList msg = null;
	private int instrumentID;

	private DataUserEventListLast(int aInstrumentID, UserEventList aMsg) {
		this.msg = aMsg;
		this.instrumentID = aInstrumentID;
	}

	static Queue<DataSend> getInstance(final DBAccess aDBAccess,
			final int aInstrumentID) {
		UserEventList.Builder msgBuilder = UserEventList.newBuilder();
		if (aDBAccess.getUserEventListLast(aInstrumentID, msgBuilder) == true) {
			// 返回队列是为了与其他消息统一（永远不会分包）
			Queue<DataSend> dataSendQueue = new LinkedList<DataSend>();
			if (true == msgBuilder.isInitialized()) {
				// 数据库连接正常，且查询结果非0
				dataSendQueue.offer(new DataUserEventListLast(aInstrumentID,
						msgBuilder.build()));
			} else {
				// 若查询结果为0则不修改队列（最终返回长度为0队列）
			}
			return dataSendQueue;
		} else {
			return null;
		}
	}

	@Override
	int getDataID() {
		return dataID;
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
		Log.println("收到回复消息" + this.getClass().getName() + "instrumentID:" + this.instrumentID);
		
	}
}

// 刷卡机用户事件列表
class DataUserEventList extends DataRecv {
	static final int dataID = MessageType.USEREVENTLIST.getNumber();
	private UserEventList msg = null;

	private DataUserEventList(UserEventList aMsg) {
		this.msg = aMsg;
	}

	static DataUserEventList checkContent(final byte[] buffer)
			throws InvalidProtocolBufferException { // 解码
		UserEventList msg = UserEventList.parseFrom(buffer);
		return new DataUserEventList(msg);
	}

	@Override
	int getDataID() { // 获取消息ID
		return dataID;
	}

	DataSend getReplyData(DBAccess aDBAccess, IONodeList.DoingForDB aDoingForDB) { // 生成回复消息，若无回复消息则返回null
		if (true == aDBAccess.setUserEventList(msg, aDoingForDB)) {
			return DataCommandSend.getDataCommandSendRecvOK();// 同步成功
		} else {
			return null;
		}
	}
}

// 最新刷卡机系统事件列表
// 实际只有一条，仅初始化时使用
class DataSystemEventListLast extends DataSend {
	static final int dataID = MessageType.SYSTEMEVENTLIST.getNumber();
	private SystemEventList msg = null;
	private int instrumentID;

	private DataSystemEventListLast(int aInstrumentID, SystemEventList aMsg) {
		this.msg = aMsg;
		this.instrumentID = aInstrumentID;
	}

	static Queue<DataSend> getInstance(final DBAccess aDBAccess,
			final int aInstrumentID) {
		SystemEventList.Builder msgBuilder = SystemEventList.newBuilder();
		if (aDBAccess.getSystemEventListLast(aInstrumentID, msgBuilder) == true) {
			// 返回队列是为了与其他消息统一（永远不会分包）
			Queue<DataSend> dataSendQueue = new LinkedList<DataSend>();
			if (true == msgBuilder.isInitialized()) {
				// 数据库连接正常，且查询结果非0
				dataSendQueue.offer(new DataSystemEventListLast(aInstrumentID,
						msgBuilder.build()));
			} else {
				// 若查询结果为0则不修改队列（最终返回长度为0队列）
			}
			return dataSendQueue;
		} else {
			return null;
		}
	}

	@Override
	int getDataID() {
		return dataID;
	}

	@Override
	byte[] getMsgBytes() {
		return msg.toByteArray();
	}

	@Override
	void setUpdated(DBAccess aDBAccess) {
		Log.println("收到回复消息" + this.getClass().getName() + "instrumentID:" + this.instrumentID);

	}
}

// 刷卡机系统事件列表
class DataSystemEventList extends DataRecv {
	static final int dataID = MessageType.SYSTEMEVENTLIST.getNumber();
	private SystemEventList msg = null;

	private DataSystemEventList(SystemEventList aMsg) {
		this.msg = aMsg;
	}

	static DataSystemEventList checkContent(final byte[] buffer)
			throws InvalidProtocolBufferException { // 解码
		SystemEventList msg = SystemEventList.parseFrom(buffer);
		return new DataSystemEventList(msg);
	}

	@Override
	int getDataID() { // 获取消息ID
		return dataID;
	}

	DataSend getReplyData(DBAccess aDBAccess, IONodeList.DoingForDB aDoingForDB) { // 生成回复消息，若无回复消息则返回null
		if (aDBAccess.setSystemEventList(msg, aDoingForDB) == true) {
			return DataCommandSend.getDataCommandSendRecvOK();// 同步成功
		} else {
			return null;
		}
	}
}

// 待同步类型枚举类（*表与仪器无关）
// // 311：用户（*） 312：账户（*） 313：资格 314：临时卡 315：仪器 316：专享时间 317：预约 318：系统日历表（*） 319：全局配置（*）
enum EUpdateType {
	UserList(0, 311), AccountList(1, 312), UserRightList(2, 313), TempCardBindingList(
			3, 314), InstrumentDetail(4, 315), ReservationList(5, 316), BookingList(
			6, 317), CalendarList(7, 318), GlobalConfig(8, 319);

	final private int updateTypeArrayIndex; // 用与枚举数组下标
	final private int updateTypeNum; // 类型ID

	private EUpdateType(int aUpdateTypeArrayIndex, int aUpdateTypeNum) {
		this.updateTypeArrayIndex = aUpdateTypeArrayIndex;
		this.updateTypeNum = aUpdateTypeNum;
	}

	// 将枚举数值转化为枚举消息类型
	static EUpdateType getTypeFromTypeNum(int aUpdateTypeNum) {
		switch (aUpdateTypeNum) {
		case 311:
			return EUpdateType.UserList;
		case 312:
			return EUpdateType.AccountList;
		case 313:
			return EUpdateType.UserRightList;
		case 314:
			return EUpdateType.TempCardBindingList;
		case 315:
			return EUpdateType.InstrumentDetail;
		case 316:
			return EUpdateType.ReservationList;
		case 317:
			return EUpdateType.BookingList;
		case 318:
			return EUpdateType.CalendarList;
		case 319:
			return EUpdateType.GlobalConfig;
		default:
			return null;
		}
	}
	
	// 将枚举消息类型转为数组编号
	// // 用于标记已同步
	int getUpdateTypeNum() {
		return this.updateTypeNum;
	}
	
	// 将枚举消息类型转为数组编号
	// // 用于将所有待同步类型通过数组同一管理（如，IONode.UpdateState中利用数组表示待同步类型呢个列表）
	int getUpdateTypeArrayIndex() {
		return this.updateTypeArrayIndex;
	}

	// 创建消息对象
	// // 查询数据库，构建待同步消息对象
	Queue<DataSend> getIOUpdateData(final DBAccess aDBAccess,
			final int aInstrumentID, final boolean aIsForInit) {
		switch (this) { // ??是否安全做法
		case UserList:
			return DataUserList.getInstance(aDBAccess, aInstrumentID,
					aIsForInit);
		case AccountList:
			return DataAccountList.getInstance(aDBAccess, aInstrumentID,
					aIsForInit);
		case UserRightList:
			return DataUserRightList.getInstance(aDBAccess, aInstrumentID,
					aIsForInit);
		case TempCardBindingList:
			return DataTempCardBindingList.getInstance(aDBAccess,
					aInstrumentID, aIsForInit);
		case InstrumentDetail:
			return DataInstrumentDetail.getInstance(aDBAccess, aInstrumentID,
					aIsForInit);
		case ReservationList:
			return DataReservationList.getInstance(aDBAccess, aInstrumentID,
					aIsForInit);
		case BookingList:
			return DataBookingList.getInstance(aDBAccess, aInstrumentID,
					aIsForInit);
		case CalendarList:
			return DataCalendarList.getInstance(aDBAccess, aInstrumentID,
					aIsForInit);
		case GlobalConfig:
			return DataGlobalConfig.getInstance(aDBAccess, aInstrumentID,
					aIsForInit);
		default:
			return null;
		}
	}
}


// **待改进

// 所有涉及dbAcess参数是否需要判断null？？
// 是否能保证消息类中的消息一定不为空？？，或者是否有地方依赖与消息一定不为空


// 数据库接口的异常与正常失败？？
// // 正常失败也回ok
// 注：访问数据库不能区分参数错误或数据库突然down掉，若刷卡机端希望回可以回error，但依然影响重发次数

// 消息分包個數判断修改：根据消息大小决定单个包里消息条数（若固定条数容易造成资源浪费）
// // 为防止消息不等长引起某个包过大错误（可对分包结果再检查）（没有必要，真的出现超大包造成到影响是刷卡机端可忽略处理，同步失败回头再同步

// **说明
// 直接调用数据库接口读取待发送列表（若出现数据库异常返回null，若待发送列表为0则返回长度为0的列表）

// 在消息包中是否处理读出消息不完整的问题（目前只有instrumentdetail需要检查，其他get都为list）
// 各个消息调试时即可发现，若消息不完整，list的add也会抛出异常

// 消息分小包（部分包可拆，如用户，账户；部分不可拆，如仪器列表）
// // 分小包放在data类中，返回队列即可（注意队列参数用前要检查）

// 已加上空包检测（若读出的list包中消息为0条则不发送）
// // 注：空包检测没有可能也可以（查出来的列表为空到几率很小，即使查出来为空也没有严重后果）


